package org.tangolyricstagger.gui;

import java.util.List;

import org.apache.log4j.Appender;
import org.apache.log4j.Logger;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.StatusLineManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.wb.swt.SWTResourceManager;
import org.slf4j.bridge.SLF4JBridgeHandler;
import org.tangolyricstagger.MatchTitlesTest;
import org.tangolyricstagger.model.GenreStatistics;
import org.tangolyricstagger.model.MatchResult;

public class MainWindow extends ApplicationWindow {
	private Action settingsAction;
	private Action startAction;
	private Action stopAction;
	private Action aboutAction;
	StyledText logOutputStyledText;
	ProgressBar progressBar;
	Label progressLabel;
	SettingsManager settingsManager;
	boolean started = false;
	MatchTitlesTest matchTitlesTest = new MatchTitlesTest();

	TabItem genreTabItem;
	TabItem matchedSongsTabItem;
	TabItem nonMatchedSongsTabItem;

	private TableViewer genreTableViewer;
	private TableViewer matchedSongsTableViewer;
	private TableViewer nonMatchedSongsTableViewer;

	/**
	 * Create the application window,
	 */
	public MainWindow() throws Exception {
		super(null);
		SLF4JBridgeHandler.removeHandlersForRootLogger();
		SLF4JBridgeHandler.install();

		createActions();
		addCoolBar(SWT.FLAT);
		addMenuBar();
		addStatusLine();

	}

	/**
	 * Create contents of the application window.
	 * 
	 * @param parent
	 */
	@Override
	protected Control createContents(Composite parent) {
		Composite container = new Composite(parent, SWT.NONE);
		container.setLayout(new RowLayout(SWT.VERTICAL));

		ActionContributionItem settingsItem = new ActionContributionItem(settingsAction);
		ActionContributionItem startItem = new ActionContributionItem(startAction);
		ActionContributionItem stopItem = new ActionContributionItem(stopAction);
		ActionContributionItem aboutItem = new ActionContributionItem(aboutAction);

		ToolBar toolBar = new ToolBar(container, SWT.FLAT | SWT.RIGHT);
		toolBar.setFont(SWTResourceManager.getFont("Segoe UI", 10, SWT.NORMAL));
		toolBar.setTouchEnabled(true);
		final ToolBarManager manager = new ToolBarManager(toolBar);
		manager.add(settingsItem);
		manager.add(startItem);
		manager.add(stopItem);
		manager.add(aboutItem);
		manager.update(true);

		Device device = Display.getCurrent();
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		Image settingsImage = new Image(device, classLoader.getResourceAsStream("img/settings.png"));
		Image startImage = new Image(device, classLoader.getResourceAsStream("img/start.png"));
		Image stopImage = new Image(device, classLoader.getResourceAsStream("img/stop.png"));
		Image aboutImage = new Image(device, classLoader.getResourceAsStream("img/about.png"));
		toolBar.getItem(0).setImage(settingsImage);
		toolBar.getItem(1).setImage(startImage);
		toolBar.getItem(2).setImage(stopImage);
		toolBar.getItem(3).setImage(aboutImage);

		TabFolder tabFolder = new TabFolder(container, SWT.NONE);
		tabFolder.setLayoutData(new RowData(935, 495));

		TabItem tabItem = new TabItem(tabFolder, SWT.NONE);
		tabItem.setText("Debug Log");

		logOutputStyledText = new StyledText(tabFolder, SWT.BORDER | SWT.WRAP | SWT.V_SCROLL);
		tabItem.setControl(logOutputStyledText);
		logOutputStyledText.setEditable(false);
		logOutputStyledText.setAlwaysShowScrollBars(true);

		Appender guiAppender = new GuiAppender(logOutputStyledText);

		genreTabItem = new TabItem(tabFolder, SWT.NONE);
		genreTabItem.setText("Genres");

		genreTableViewer = new TableViewer(tabFolder, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
		createGenreColumns(tabFolder, genreTableViewer);
		genreTableViewer.setContentProvider(new ArrayContentProvider());
		// Get the content for the viewer, setInput will call getElements in the contentProvider
		genreTableViewer.setInput(GenreModelProvider.INSTANCE.getGenres());
		genreTableViewer.getTable().setHeaderVisible(true);
		GridData gridData = new GridData();
		gridData.verticalAlignment = GridData.FILL;
		gridData.horizontalSpan = 2;
		gridData.grabExcessHorizontalSpace = true;
		gridData.grabExcessVerticalSpace = true;
		gridData.horizontalAlignment = GridData.FILL;
		genreTableViewer.getControl().setLayoutData(gridData);
		genreTabItem.setControl(genreTableViewer.getTable());

		matchedSongsTabItem = new TabItem(tabFolder, SWT.NONE);
		matchedSongsTabItem.setText("Matched Songs");

		matchedSongsTableViewer = new TableViewer(tabFolder, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
		createMatchResultsColumns(tabFolder, matchedSongsTableViewer);
		matchedSongsTableViewer.getTable().setHeaderVisible(true);
		matchedSongsTableViewer.setContentProvider(new ArrayContentProvider());
		// Get the content for the viewer, setInput will call getElements in the contentProvider
		matchedSongsTableViewer.setInput(MatchResultModelProvider.INSTANCE.getMatchResults());
		GridData gridDataMatchedSongs = new GridData();
		gridDataMatchedSongs.verticalAlignment = GridData.FILL;
		gridDataMatchedSongs.horizontalSpan = 2;
		gridDataMatchedSongs.grabExcessHorizontalSpace = true;
		gridDataMatchedSongs.grabExcessVerticalSpace = true;
		gridDataMatchedSongs.horizontalAlignment = GridData.FILL;
		matchedSongsTableViewer.getControl().setLayoutData(gridDataMatchedSongs);
		matchedSongsTabItem.setControl(matchedSongsTableViewer.getTable());

		nonMatchedSongsTabItem = new TabItem(tabFolder, SWT.NONE);
		nonMatchedSongsTabItem.setText("Non-Matched Songs");

		nonMatchedSongsTableViewer = new TableViewer(tabFolder, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
		createNonMatchResultsColumns(tabFolder, nonMatchedSongsTableViewer);
		nonMatchedSongsTableViewer.getTable().setHeaderVisible(true);
		nonMatchedSongsTableViewer.setContentProvider(new ArrayContentProvider());
		// Get the content for the viewer, setInput will call getElements in the contentProvider
		nonMatchedSongsTableViewer.setInput(MatchResultModelProvider.INSTANCE.getNonMatchResults());
		GridData gridDataNonMatchedSongs = new GridData();
		gridDataNonMatchedSongs.verticalAlignment = GridData.FILL;
		gridDataNonMatchedSongs.horizontalSpan = 2;
		gridDataNonMatchedSongs.grabExcessHorizontalSpace = true;
		gridDataNonMatchedSongs.grabExcessVerticalSpace = true;
		gridDataNonMatchedSongs.horizontalAlignment = GridData.FILL;
		nonMatchedSongsTableViewer.getControl().setLayoutData(gridDataNonMatchedSongs);
		nonMatchedSongsTabItem.setControl(nonMatchedSongsTableViewer.getTable());

		Composite progressContainer = new Composite(container, SWT.NONE);
		progressContainer.setLayout(new RowLayout(SWT.HORIZONTAL));

		progressBar = new ProgressBar(progressContainer, SWT.NONE);
		progressBar.setLayoutData(new RowData(640, SWT.DEFAULT));
		progressBar.setSelection(0);

		progressLabel = new Label(progressContainer, SWT.NONE);
		progressLabel.setLayoutData(new RowData(300, SWT.DEFAULT));
		progressLabel.setText("");

		progressContainer.pack();
		container.pack();

		Logger.getRootLogger().addAppender(guiAppender);

		getShell().addShellListener(new ShellAdapter() {
			@Override
			public void shellActivated(ShellEvent shellevent) {
				if (!started) {
					Shell shell = (Shell) shellevent.getSource();
					shell.setVisible(true);
					started = true;
					settingsManager = new SettingsManager();
					if (!settingsManager.loadSettings()) {
						MessageDialog.openInformation(shell, "First time usage",
						        "It appears you've started me for the first time. I'll bring you to the settings menu right away.");
						settingsAction.run();
					}
				}
			}
		});

		return container;
	}

	/**
	 * Create the actions.
	 */
	private void createActions() {
		// Create the actions
		{
			startAction = new Action("Start") {
				public void run() {
					new Thread(new Runnable() {
						public void run() {
							matchTitlesTest.testMatchTitles(MainWindow.this, settingsManager.getSettings(), progressBar, progressLabel,
							        GenreModelProvider.INSTANCE, MatchResultModelProvider.INSTANCE);
						}
					}).start();

					System.out.println("async executed;");
				}
			};

			stopAction = new Action("Stop") {
				public void run() {
					matchTitlesTest.requestStop();
				}
			};

			settingsAction = new Action("Settings") {
				public void run() {
					SettingsDialog settingsDialog = new SettingsDialog(getShell(), settingsManager);
					settingsDialog.open();
				}
			};

			aboutAction = new Action("About") {
				public void run() {
					new AboutDialog(getShell()).open();
				}
			};

		}
	}

	/**
	 * Create the status line manager.
	 * 
	 * @return the status line manager
	 */
	@Override
	protected StatusLineManager createStatusLineManager() {
		StatusLineManager statusLineManager = new StatusLineManager();
		return statusLineManager;
	}

	/**
	 * Launch the application.
	 * 
	 * @param args
	 */
	public static void main(String args[]) {
		try {
			MainWindow window = new MainWindow();
			window.setBlockOnOpen(true);
			window.open();
			Display.getCurrent().dispose();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Configure the shell.
	 * 
	 * @param newShell
	 */
	@Override
	protected void configureShell(Shell newShell) {
		super.configureShell(newShell);
		newShell.setText("TangoLyricsTagger 0.91");
	}

	/**
	 * Return the initial size of the window.
	 */
	@Override
	protected Point getInitialSize() {
		return new Point(964, 662);
	}

	private void createGenreColumns(final Composite parent, final TableViewer viewer) {
		String[] titles = { "Genre", "Count" };
		int[] bounds = { 300, 100 };

		// First column
		TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				GenreStatistics g = (GenreStatistics) element;
				return g.getGenre();
			}
		});

		// Second column
		col = createTableViewerColumn(titles[1], bounds[1], 1, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				GenreStatistics g = (GenreStatistics) element;
				if (g.getCount() == null) {
					return "(ignored)";
				} else {
					return "" + g.getCount();
				}
			}
		});

	}

	private void createMatchResultsColumns(final Composite parent, final TableViewer viewer) {
		String[] titles = { "Title", "Todotango Title", "Levenshtein", "Lyrics Exist", "Lyrics" };
		int[] bounds = { 230, 230, 80, 80, 290 };

		// First column
		TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				MatchResult mr = (MatchResult) element;
				return mr.getOriginalTitle();
			}
		});

		// Second column
		col = createTableViewerColumn(titles[1], bounds[1], 1, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				MatchResult mr = (MatchResult) element;
				if (mr.isMatched()) {
					if (mr.getTodoTangoTitle() != null) {
						return mr.getTodoTangoTitle();
					} else {
						return mr.getMatchedTitle();
					}
				} else {
					return "-";
				}
			}
		});

		// Third column
		col = createTableViewerColumn(titles[2], bounds[2], 2, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				MatchResult mr = (MatchResult) element;
				return "" + mr.getSimilarity();
			}
		});

		// Forth column
		col = createTableViewerColumn(titles[3], bounds[3], 3, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				MatchResult mr = (MatchResult) element;
				return mr.isLyricsFound() ? "YES" : "NO";
			}
		});

		// Fifth column
		col = createTableViewerColumn(titles[4], bounds[4], 4, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				MatchResult mr = (MatchResult) element;
				return mr.getLyrics();
			}
		});
	}

	private void createNonMatchResultsColumns(final Composite parent, final TableViewer viewer) {
		String[] titles = { "Title", "Cleaned Title", "Closest Todotango cleaned title (discarded)", "Levenshtein" };
		int[] bounds = { 280, 280, 270, 80 };

		// First column
		TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				MatchResult mr = (MatchResult) element;
				return mr.getOriginalTitle();
			}
		});

		// Second column
		col = createTableViewerColumn(titles[1], bounds[1], 1, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				MatchResult mr = (MatchResult) element;
				return mr.getCandidateTitle();
			}
		});

		// Third column
		col = createTableViewerColumn(titles[2], bounds[2], 2, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				MatchResult mr = (MatchResult) element;
				return mr.getMatchedTitle();
			}
		});

		// Forth column
		col = createTableViewerColumn(titles[3], bounds[3], 3, viewer);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				MatchResult mr = (MatchResult) element;
				return "" + mr.getSimilarity();
			}
		});
	}

	private TableViewerColumn createTableViewerColumn(String title, int bound, final int colNumber, TableViewer viewer) {
		final TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE);
		final TableColumn column = viewerColumn.getColumn();
		column.setText(title);
		column.setWidth(bound);
		column.setResizable(true);
		column.setMoveable(true);
		return viewerColumn;
	}

	public void refreshGenres() {
		Display.getDefault().asyncExec(new Runnable() {
			public void run() {
				genreTableViewer.refresh();
				List<?> rows = (List<?>) genreTableViewer.getInput();
				genreTabItem.setText("Genres (" + rows.size() + ")");
			}
		});
	}

	public void refreshMatchResults() {
		Display.getDefault().asyncExec(new Runnable() {
			public void run() {
				matchedSongsTableViewer.refresh();
				List<?> matchedRows = (List<?>) matchedSongsTableViewer.getInput();
				matchedSongsTabItem.setText("Matched Songs (" + matchedRows.size() + ")");
				nonMatchedSongsTableViewer.refresh();
				List<?> nonMatchedRows = (List<?>) nonMatchedSongsTableViewer.getInput();
				nonMatchedSongsTabItem.setText("Non-Matched Songs (" + nonMatchedRows.size() + ")");
			}
		});
	}
}
